/*-------------------------------------------------------------------------
 *
 * parse_node.c
 *	  为查询树创建节点的各种例程
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/parser/parse_node.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/htup_details.h"
#include "access/table.h"
#include "catalog/pg_type.h"
#include "mb/pg_wchar.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/subscripting.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
#include "parser/parsetree.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/varbit.h"

static void fc_pcb_error_callback(void *fc_arg);


/*
 * make_parsestate
 *		分配并初始化一个新的 ParseState。
 *
 * 调用者最终应通过 free_parsestate() 释放 ParseState。
 */
ParseState *
make_parsestate(ParseState *fc_parentParseState)
{
	ParseState *fc_pstate;

	fc_pstate = palloc0(sizeof(ParseState));

	fc_pstate->parentParseState = fc_parentParseState;

	/* 填充未从 null/false/zero 开始的字段 */
	fc_pstate->p_next_resno = 1;
	fc_pstate->p_resolve_unknowns = true;

	if (fc_parentParseState)
	{
		fc_pstate->p_sourcetext = fc_parentParseState->p_sourcetext;
		/* 所有钩子都从父级复制 */
		fc_pstate->p_pre_columnref_hook = fc_parentParseState->p_pre_columnref_hook;
		fc_pstate->p_post_columnref_hook = fc_parentParseState->p_post_columnref_hook;
		fc_pstate->p_paramref_hook = fc_parentParseState->p_paramref_hook;
		fc_pstate->p_coerce_param_hook = fc_parentParseState->p_coerce_param_hook;
		fc_pstate->p_ref_hook_state = fc_parentParseState->p_ref_hook_state;
		/* 查询环境在整个解析分析中保持上下文 */
		fc_pstate->p_queryEnv = fc_parentParseState->p_queryEnv;
	}

	return fc_pstate;
}

/*
 * free_parsestate
 *		释放一个 ParseState 及其任何附属资源。
 */
void free_parsestate(ParseState *fc_pstate)
{
	/*
	 * 检查我们是否没有产生太多的 resnos；至少我们
	 * 不能允许超过 2^16，因为这将超出 AttrNumber 的范围。
	 * 使用 MaxTupleAttributeNumber 似乎最安全。
	 */
	if (fc_pstate->p_next_resno - 1 > MaxTupleAttributeNumber)
		ereport(ERROR,
				(errcode(ERRCODE_TOO_MANY_COLUMNS),
				 errmsg("target lists can have at most %d entries",
						MaxTupleAttributeNumber)));

	if (fc_pstate->p_target_relation != NULL)
		table_close(fc_pstate->p_target_relation, NoLock);

	pfree(fc_pstate);
}


/*
 * parser_errposition
 *		报告解析分析时的光标位置（如果可能）。
 *
 * 这预计将在 ereport() 调用中使用。返回值
 * 是一个虚拟值（实际上总是 0）。
 *
 * 存储在原始解析树中的位置是源
 * 字符串的字节偏移量。我们必须将它们转换为基于 1 的字符索引，以便报告
 * 给客户端。（我们这样做是为了避免在
 * 常规非错误情况下的不必要开销：计算字符索引将比存储标记偏移量更昂贵。）
 */
int parser_errposition(ParseState *fc_pstate, int fc_location)
{
	int			fc_pos;

	/* 如果未提供位置则无操作 */
	if (fc_location < 0)
		return 0;
	/* 如果源文本不可用，则无法执行任何操作 */
	if (fc_pstate == NULL || fc_pstate->p_sourcetext == NULL)
		return 0;
	/* 将偏移量转换为字符编号 */
	fc_pos = pg_mbstrlen_with_len(fc_pstate->p_sourcetext, fc_location) + 1;
	/*
 * 在解析完成后调用，以清理 plpgsql_scanner_init() 的残余。
 */
	return errposition(fc_pos);
}


/*
 * setup_parser_errposition_callback
 *		安排非解析器错误报告错误位置
 *
 * 有时解析器调用不属于解析器
 * 子系统的函数，合理地不能传递 ParseState；但我们希望
 * 在这些函数中抛出的任何错误都带有解析
 * 错误位置的标签。 使用此函数设置一个错误上下文堆栈
 * 条目，以实现此目的。 使用模式：
 *
 *		声明一个局部变量 "ParseCallbackState pcbstate"
 *		...
 *		setup_parser_errposition_callback(&pcbstate, pstate, location);
 *		调用可能抛出错误的函数；
 *		cancel_parser_errposition_callback(&pcbstate);
 */
void setup_parser_errposition_callback(ParseCallbackState *fc_pcbstate,
								  ParseState *fc_pstate, int fc_location)
{
	/* 为ereport()设置错误追踪支持 */
	fc_pcbstate->pstate = fc_pstate;
	fc_pcbstate->location = fc_location;
	fc_pcbstate->errcallback.callback = fc_pcb_error_callback;
	fc_pcbstate->errcallback.arg = (void *) fc_pcbstate;
	fc_pcbstate->errcallback.previous = error_context_stack;
	error_context_stack = &fc_pcbstate->errcallback;
}

/*
 * 取消先前设定的错误位置回调。
 */
void cancel_parser_errposition_callback(ParseCallbackState *fc_pcbstate)
{
	/* 弹出错误上下文栈 */
	error_context_stack = fc_pcbstate->errcallback.previous;
}

/*
 * 插入解析器错误位置的错误上下文回调。
 *
 * 注意，这将在安装回调期间发生的 *任何* 错误时被调用。我们避免插入不相关的错误位置，如果错误是查询取消——还有其他重要情况吗？
 */
static void fc_pcb_error_callback(void *fc_arg)
{
	ParseCallbackState *fc_pcbstate = (ParseCallbackState *) fc_arg;

	if (geterrcode() != ERRCODE_QUERY_CANCELED)
		(void) parser_errposition(fc_pcbstate->pstate, fc_pcbstate->location);
}


/*
 * transformContainerType()
 * 识别用于下标操作的实际容器类型。
 *
 * containerType/containerTypmod 在必要时会被修改，以识别实际的容器类型和 typmod。 这主要涉及将任何域压缩到其基本类型，但还有一些特殊考虑。 需要注意的是，调用者仍需检查结果类型是否为容器。
 */
void transformContainerType(Oid *fc_containerType, int32 *fc_containerTypmod)
{
	/*
 * 如果输入是一个域，将其压缩为基本类型，并提取出要应用于基本类型的实际 typmod。对域进行下标操作是一项必然作用于基本容器类型的操作，而不是域本身。(请注意，我们没有提供任何方法，使得在容器类型上创建域的创建者能够隐藏其下标的能力。)
 */
	*fc_containerType = getBaseTypeAndTypmod(*fc_containerType, fc_containerTypmod);

	/*
 * 我们将 int2vector 和 oidvector 视为 class="domain"
 * over int2[] 和 oid[]。这很重要，因为数组切片可能会创建一个不满足 xxxvector 类型的维度约束的数组；因此，我们希望切片操作的结果被视为更通用的类型。
 */
	if (*fc_containerType == INT2VECTOROID)
		*fc_containerType = INT2ARRAYOID;
	else if (*fc_containerType == OIDVECTOROID)
		*fc_containerType = OIDARRAYOID;
}

/*
 * transformContainerSubscripts()
 * 转换容器（数组等）下标。这用于容器获取和容器赋值。
 *
 * 在容器获取中，我们给定一个源容器值，并生成一个表示提取单个容器元素或容器切片结果的表达式。
 *
 * 容器赋值在这里基本上与容器获取的处理相同。调用者将修改结果节点以插入要分配给提取将检索的元素或切片的源值。执行结果将是一个新的容器值，源值插入到容器的适当部分。
 *
 * 在这两种情况下，如果源是容器类型上的域，则结果与其为容器类型时相同；本质上，我们必须在应用下标操作之前将域折叠为其基本类型。 （注意这里将 int2vector 和 oidvector 视为域。）
 *
 * pstate 解析状态
 * containerBase 已转换的整个容器表达式
 * containerType 容器数据类型的 OID（应与 containerBase 的类型匹配，或是 containerBase 的域类型的基本类型）
 * containerTypMod 容器的 typmod
 * indirection 未转化的下标列表（不能为 NIL）
 * isAssignment 如果这将成为容器赋值，则为真。
 */
SubscriptingRef *
transformContainerSubscripts(ParseState *fc_pstate,
							 Node *fc_containerBase,
							 Oid fc_containerType,
							 int32 fc_containerTypMod,
							 List *fc_indirection,
							 bool fc_isAssignment)
{
	SubscriptingRef *fc_sbsref;
	const SubscriptRoutines *fc_sbsroutines;
	Oid			fc_elementType;
	bool		fc_isSlice = false;
	ListCell   *fc_idx;

	/*
 * 确定实际容器类型，压缩任何域。在赋值的情况下，调用者已经执行了此操作，因为它还需要知道实际容器类型。
 */
	if (!fc_isAssignment)
		transformContainerType(&fc_containerType, &fc_containerTypMod);

	/*
 * 验证容器类型是否可以下标，并获取其支持函数和 typelem。
 */
	fc_sbsroutines = getSubscriptingRoutines(fc_containerType, &fc_elementType);
	if (!fc_sbsroutines)
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
				 errmsg("cannot subscript type %s because it does not support subscripting",
						format_type_be(fc_containerType)),
				 parser_errposition(fc_pstate, exprLocation(fc_containerBase))));

	/*
 * 检测 indirection 项中是否有切片指示符。
 *
 * 仅包含简单下标的列表指的是单个容器元素。如果有任何项是切片指示符（lower:upper），那么下标表达式意味着一个容器切片操作。
 */
	foreach(fc_idx, fc_indirection)
	{
		A_Indices  *fc_ai = lfirst_node(A_Indices, fc_idx);

		if (fc_ai->is_slice)
		{
			fc_isSlice = true;
			break;
		}
	}

	/*
 * 准备构建 SubscriptingRef 节点。
 */
	fc_sbsref = makeNode(SubscriptingRef);

	fc_sbsref->refcontainertype = fc_containerType;
	fc_sbsref->refelemtype = fc_elementType;
	/* refrestype 将由特定容器逻辑设置 */
	fc_sbsref->reftypmod = fc_containerTypMod;
	/* refcollid 将由 parse_collate.c 设置 */
	/* refupperindexpr, reflowerindexpr 将由容器逻辑设置 */
	fc_sbsref->refexpr = (Expr *) fc_containerBase;
	fc_sbsref->refassgnexpr = NULL;	/* 如果是赋值，调用者将填充 */

	/*
 * 调用特定容器类型的逻辑以转换下标并确定下标结果类型。
 */
	fc_sbsroutines->transform(fc_sbsref, fc_indirection, fc_pstate,
						   fc_isSlice, fc_isAssignment);

	/*
 * 验证我们得到了有效类型（例如，这可以防止某些人使用 array_subscript_handler 作为 typsubscript，而不设置 typelem）。
 */
	if (!OidIsValid(fc_sbsref->refrestype))
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
				 errmsg("cannot subscript type %s because it does not support subscripting",
						format_type_be(fc_containerType))));

	return fc_sbsref;
}



/*
 * make_const
 *
 * 将一个 A_Const 节点（由语法返回）转换为一个 Const 节点，
 * 其类型为常量的“自然”类型。请注意，这个例程仅在常量没有
 * 显式转换时使用，因此我们必须猜测所需的类型。
 *
 * 对于字符串字面量，我们生成一个类型为 UNKNOWN 的常量——
 * 其表示形式与 cstring 相同，但它向后续类型解析表明我们尚不确定
 * 应该被视为什么类型。显式的 “NULL” 常量也被标记为 UNKNOWN。
 *
 * 对于整数和浮点数，我们根据数字的值生成 int4、int8 或 numeric。
 * XXX 我们也应该生成 int2，但在那之前需要额外的清理；
 * 如果尝试这样做，出现的例子太多，很可能会失败。
 */
Const * make_const(ParseState *fc_pstate, A_Const *fc_aconst)
{
	Const	   *fc_con;
	Datum		fc_val;
	Oid			fc_typeid;
	int			fc_typelen;
	bool		fc_typebyval;
	ParseCallbackState fc_pcbstate;

	if (fc_aconst->isnull)
	{
		/* 返回一个 null 常量 */
		fc_con = makeConst(UNKNOWNOID,
						-1,
						InvalidOid,
						-2,
						(Datum) 0,
						true,
						false);
		fc_con->location = fc_aconst->location;
		return fc_con;
	}

	switch (nodeTag(&fc_aconst->val))
	{
		case T_Integer:
			fc_val = Int32GetDatum(intVal(&fc_aconst->val));

			fc_typeid = INT4OID;
			fc_typelen = sizeof(int32);
			fc_typebyval = true;
			break;

		case T_Float:
			{
				/* 也可能是一个超大整数或浮点数 ... */

				int64		fc_val64;
				char	   *fc_endptr;

				errno = 0;
				fc_val64 = strtoi64(fc_aconst->val.fval.fval, &fc_endptr, 10);
				if (errno == 0 && *fc_endptr == '\0')
				{
					/*
					 * 它实际上可能适合 int32。可能只有 INT_MIN
					 * 可以出现，但我们将通用地进行测试以确保。
					 */
					int32		fc_val32 = (int32) fc_val64;

					if (fc_val64 == (int64) fc_val32)
					{
						fc_val = Int32GetDatum(fc_val32);

						fc_typeid = INT4OID;
						fc_typelen = sizeof(int32);
						fc_typebyval = true;
					}
					else
					{
						fc_val = Int64GetDatum(fc_val64);

						fc_typeid = INT8OID;
						fc_typelen = sizeof(int64);
						fc_typebyval = FLOAT8PASSBYVAL;	/* int8 和 float8 相同 */
					}
				}
				else
				{
					/* 安排在 numeric_in() 失败时报告位置 */
					setup_parser_errposition_callback(&fc_pcbstate, fc_pstate, fc_aconst->location);
					fc_val = DirectFunctionCall3(numeric_in,
											  CStringGetDatum(fc_aconst->val.fval.fval),
											  ObjectIdGetDatum(InvalidOid),
											  Int32GetDatum(-1));
					cancel_parser_errposition_callback(&fc_pcbstate);

					fc_typeid = NUMERICOID;
					fc_typelen = -1;	/* 可变长度 */
					fc_typebyval = false;
				}
				break;
			}

		case T_Boolean:
			fc_val = BoolGetDatum(boolVal(&fc_aconst->val));

			fc_typeid = BOOLOID;
			fc_typelen = 1;
			fc_typebyval = true;
			break;

		case T_String:

			/*
			 * 我们在这里假设 UNKNOWN 的内部表示与
			 * CSTRING 相同
			 */
			fc_val = CStringGetDatum(strVal(&fc_aconst->val));

			fc_typeid = UNKNOWNOID;	/* 稍后会被强制转换 */
			fc_typelen = -2;		/* cstring-style varwidth type */
			fc_typebyval = false;
			break;

		case T_BitString:
			/* 安排在 bit_in() 失败时报告位置 */
			setup_parser_errposition_callback(&fc_pcbstate, fc_pstate, fc_aconst->location);
			fc_val = DirectFunctionCall3(bit_in,
									  CStringGetDatum(fc_aconst->val.bsval.bsval),
									  ObjectIdGetDatum(InvalidOid),
									  Int32GetDatum(-1));
			cancel_parser_errposition_callback(&fc_pcbstate);
			fc_typeid = BITOID;
			fc_typelen = -1;
			fc_typebyval = false;
			break;

		default:
			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(&fc_aconst->val));
			return NULL;		/* 保持编译器安静 */
	}

	fc_con = makeConst(fc_typeid,
					-1,			/* typmod -1 对所有情况都是可以的 */
					InvalidOid, /* 所有情况都是不可比较类型 */
					fc_typelen,
					fc_val,
					false,
					fc_typebyval);
	fc_con->location = fc_aconst->location;

	return fc_con;
}
